package com.service.login.service.impl;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.service.config.constant.Constant;
import com.service.config.utils.*;
import com.service.login.annotation.DataSource;
import com.service.login.bean.MailBean;
import com.service.login.controller.UserInfoController;
import com.service.login.dao.ScreenUserDao;
import com.service.login.dao.UserExtendDao;
import com.service.login.dto.*;
import com.service.login.entity.UserExtend;
import com.service.login.entity.UserInfo;
import com.service.login.dao.UserInfoDao;
import com.service.login.enums.DataSourceType;
import com.service.login.service.FeginMstsc;
import com.service.login.service.FeginMstscBsAdmin;
import com.service.login.service.FeginMstscUnique;
import com.service.login.service.UserInfoService;


import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

import static com.service.config.constant.ApiUrlConstants.*;
import static com.service.config.constant.Constant.*;

/**
 * 用户表(UserInfo)表服务实现类
 *
 * @author makejava
 * @since 2021-03-30 11:37:21
 */
@Service("userInfoService")
@Slf4j
public class UserInfoServiceImpl implements UserInfoService {
    @Resource
    private UserInfoDao userInfoDao;
    @Resource
    private UserExtendDao userExtendDao;
    @Resource
    private RedisUtil redisUtil;
    @Autowired
    RedisTemplate redisTemplate;
    @Resource
    RestTemplate restTemplate;
    @Autowired
    private FeginMstsc feginMstsc;
    @Resource
    private ScreenUserDao screenUserDao;
    @Value("${Spring.http.url}")
    private String url;
    @Autowired
    private UserInfoService userInfoService;
    @Resource
    private FeginMstscBsAdmin feginMstscBsAdmin;
    @Resource
    private FeginMstscUnique feginMstscUnique;

    @Resource
    private AsyncService asyncService;
    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    @DataSource(DataSourceType.MASTER)
    public UserInfo queryById(String id) {
        return this.userInfoDao.queryById(id);
    }

    /**
     * 查询多条数据
     *
     * @param offset 查询起始位置
     * @param limit  查询条数
     * @return 对象列表
     */
    @Override
    @DataSource(DataSourceType.MASTER)
    public List<UserInfo> queryAllByLimit(int offset, int limit) {
        return this.userInfoDao.queryAllByLimit(offset, limit);
    }

    /**
     * 新增数据
     *
     * @param userInfo 实例对象
     * @return 实例对象
     */
    @Override
    @DataSource(DataSourceType.MASTER)
    public UserInfo insert(UserInfo userInfo) {
        this.userInfoDao.insert(userInfo);
        return userInfo;
    }

    /**
     * 修改数据
     *
     * @param userInfo 实例对象
     * @return 实例对象
     */
    @Override
    @DataSource(DataSourceType.MASTER)
    public UserInfo update(UserInfo userInfo) {
        this.userInfoDao.update(userInfo);
        return this.queryById(userInfo.getId());
    }

    /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 是否成功
     */
    @Override
    @DataSource(DataSourceType.MASTER)
    public boolean deleteById(String id) {
        return this.userInfoDao.deleteById(id) > 0;
    }

    @Override
    @DataSource(DataSourceType.MASTER)
    public UserInfo queryAll(LoginUserDto loginUserDto)  {
        try {
            return userInfoDao.queryUserInfo(loginUserDto.getAccount(),String.format(Locale.ROOT,"%s%s%s", Constant.CIPHER_PREFIX,Constant.COLON,DesUtil.encrypt(loginUserDto.getPassword())));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    @DataSource(DataSourceType.MASTER)
    public UserInfo register(UserInfoDto userInfoDto, String realIp) {
        return getUserInfo(userInfoDto,realIp);
    }

    /**
     * 找回密码 根据用户名找用户 用户名是唯一的
     * 所以判断该用户是否存在
     * @param username
     * @return
     */
    @Override
    @DataSource(DataSourceType.MASTER)
    public UserInfo queryByUserName(UserInfoDto username) throws Exception {
        return getUserInfoDto(username);
    }

    @Override
    public void sendMail(UserInfoDto userInfo) {
        MailBean mailBean = new MailBean();
        mailBean.setSubject("密码找回");
        mailBean.setRecipient(userInfo.getUserExtend().getEmail());
        mailBean.setContent("您的密码："+userInfo.getPassword());
        redisTemplate.convertAndSend("sendEmail",mailBean); // 发送邮件
    }

    @Override
    @DataSource(DataSourceType.SLAVE)
    public UserInfo queryAlls(LoginUserDto loginUserDto) {
        UserInfo userInfo = userInfoDao.queryUserInfo(loginUserDto.getAccount(), loginUserDto.getPassword());
        return userInfo;
    }

    @Override
    @DataSource(DataSourceType.SLAVE)
    public UserInfo mspRegister(UserInfoDto userInfoDto, String realIp) {
        return getUserInfo(userInfoDto, realIp);
    }

    @Override
    @DataSource(DataSourceType.SLAVE)
    public void updateMsp(UserInfo strictMap) {
        userInfoDao.update(strictMap);
    }

    @Override
    @DataSource(DataSourceType.SLAVE)
    public UserInfo queryByMspUserName(UserInfoDto username) throws Exception {
        return getUserInfoDto(username);
    }

    /**
     * 用户登录获取token
     * @param loginUserDto
     * @param serviceId
     * @param httpServletRequest
     * @return
     * @throws Exception
     */
    @Override
    public HashMap getToken(LoginUserDto loginUserDto, String serviceId, HttpServletRequest httpServletRequest) throws Exception {
        HashMap map = new HashMap();
        if (serviceId .equals("2")) {
            UserInfo userInfo = userInfoService.queryAlls(loginUserDto);
            if (userInfo == null) {
                return null;
            }
            if (userInfo.getStatus().equals("2") || userInfo.getStatus().equals("3")) {
                map.put("errorCode","您的账号已被封号，详情请查看您的邮箱");
                return map;
            }
            return getHashMap(loginUserDto, httpServletRequest, map, userInfo);
        }else {
            UserInfo userInfo = userInfoService.queryAll(loginUserDto);
            if (userInfo == null) {
                return null;
            }
            if (userInfo.getStatus().equals("2") || userInfo.getStatus().equals("3")) {
                map.put("errorCode","您的账号已被封号，详情请查看您的邮箱");
                return map;
            }
            return getHashMap(loginUserDto, httpServletRequest, map, userInfo);
        }
    }

    /**
     * 大屏用户 datasource
     * @param simplifyUserInfoDto
     * @param realIp
     * @return
     * @throws Exception
     */
    @Override
    @DataSource(DataSourceType.MASTER)
    public Result simplifyRegister(SimplifyUserInfoDto simplifyUserInfoDto, String realIp) throws Exception {
        return getResult(simplifyUserInfoDto, realIp);
    }
    /**
     * 校验云管api
     * @param checkTenantDto
     * @return
     */
    public Map check(CheckTenantDto checkTenantDto) {
        HashMap map = new HashMap();
        ScreenUser screenUser = new ScreenUser();
        screenUser.setName(checkTenantDto.getUsername());
        List<ScreenUser> screenUsers = screenUserDao.queryAll(screenUser);
        //该账号是否存在
        if (screenUsers.size() > 0) {
            map.put("errorMsg","关联华为云管理账号已存在");
        }else {
            HttpHeaders requestHeaders = new HttpHeaders();
            MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
            requestHeaders.setContentType(type);
            requestHeaders.add("Accept", MediaType.APPLICATION_JSON.toString());
            String token = getToken(checkTenantDto.getUsername(), checkTenantDto.getPassword(), requestHeaders, checkTenantDto.getDomain());
            if (token == null) {
                map.put("errorMsg", "华为云管理账号不存在");
            } else {
                //获取站点
                requestHeaders.add("X-AUTH-TOKEN", token);
                List<SiteDto> sites = getSites(requestHeaders, checkTenantDto.getDomain());
                if (sites != null && sites.size() > 0) {
                    return map;
                }
                map.put("errorMsg", "该账号为租户账号，请使用正确的Api账号");
            }
        }
        return map;
    }

    /**
     * msp用户 datasource
     * @param simplifyUserInfoDto
     * @param realIp
     * @return
     */
    @Override
    @DataSource(DataSourceType.SLAVE)
    public Result MspSimplifyRegister(SimplifyUserInfoDto simplifyUserInfoDto, String realIp) throws Exception {
        return getResult(simplifyUserInfoDto, realIp);
    }

    /**
     * 抽取公共代码
     * @param simplifyUserInfoDto
     * @param realIp
     * @return
     */
    private Result getResult(SimplifyUserInfoDto simplifyUserInfoDto, String realIp) throws Exception {
        HashMap map = new HashMap();
        UserInfo userInfo = userInfoDao.queryUserByName(simplifyUserInfoDto.getApiUsername());
        if (userInfo != null) {
            return Result.failure(USERNAME_REPEAT,"用户名已存在，请重新输入");
        }else {
            CheckTenantDto checkTenantDto = new CheckTenantDto();
            checkTenantDto.setDomain(simplifyUserInfoDto.getApiAddress());
            checkTenantDto.setPassword(simplifyUserInfoDto.getApiPassword());
            checkTenantDto.setUsername(simplifyUserInfoDto.getApiUsername());
            UserInfoDto userInfo1 = new UserInfoDto();
            userInfo1.setUsername(simplifyUserInfoDto.getApiUsername());
            userInfo1.setPassword(simplifyUserInfoDto.getApiPassword());
            UserInfo userInfo2 = getUserInfo(userInfo1, realIp); // 新增用户
            if (userInfo2 != null) {
                ScreenUserDto screenUserDto = testScreenUser(simplifyUserInfoDto,userInfo2);
                if (screenUserDto == null) {
                    userInfoService.deleteById(userInfo2.getId());
                    return Result.failure(10013, "云管理api账号已经存在，请检查");
                }
                Map check = check(checkTenantDto); // 校验云管api
                if (check.containsKey("errorMsg")) {
                    screenUserDao.deleteById(screenUserDto.getId());
                    userInfoService.deleteById(userInfo2.getId());
                    return Result.ok().setData(check);
                }
                String token = UserTokenManager.generateToken(userInfo2.getId());
                UserDto userDto = ModelMapperUtil.strictMap(userInfo2, UserDto.class);
                userDto.setIp(realIp); // 来源ip
                userDto.setDate(new Date()); //登录时间
                redisUtil.setValue(token+userInfo2.getId(), JsonXMLUtils.obj2json(userInfo2)); // 存入缓存
                map.put("Token", token);
                asyncService.asyncMethod(token,userInfo2,screenUserDto);
                map.put("success", "您已注册成功，请等待一段时间会有数据刷新");
                return Result.ok().setData(map);
            }
            return Result.failure(USERNAME_REPEAT,"用户名已存在，请重新输入");
        }
    }

    /**
     * 校验api账号是否存在
     * @param simplifyUserInfoDto
     * @param userInfo2
     * @return
     */
   private ScreenUserDto testScreenUser(SimplifyUserInfoDto simplifyUserInfoDto,UserInfo userInfo2){
       ScreenUserDto screenUser = new ScreenUserDto();
       screenUser.setTenantName(simplifyUserInfoDto.getApiUsername());
       screenUser.setDomain(simplifyUserInfoDto.getApiAddress());
       screenUser.setTenantPwd(simplifyUserInfoDto.getApiPassword());
       Result result = feginMstscUnique.addApiAccount();
       if (result.getCode() == 200) {
           screenUser.setId(result.getData().toString());
       }else {
           screenUser.setId(IdUtil.getStringId());
       }
       screenUser.setUserId(userInfo2.getId());
       ScreenUser screenUser1 = ModelMapperUtil.strictMap(screenUser, ScreenUser.class);
       List<ScreenUser> screenUsers = screenUserDao.queryAll(new ScreenUser());

       //该账号是否存在
       if (screenUsers.size() > 0) {
           Boolean flag = screenUsers.stream().anyMatch(task -> task.getTenantName().equals(screenUser.getTenantName()));
           if (flag) {
               return null;
           }
       }
       screenUser1.setStatus(true);
       screenUserDao.insert(screenUser1);
       return screenUser;
    }

    /**
     * 获取token
     *
     * @param username
     * @param password
     * @param requestHeaders
     * @return
     */
    public String getToken(String username, String password, HttpHeaders requestHeaders, String domain) {
        //获取token
        Map map = new HashMap();
        map.put("userName", username);
        map.put("password", password);
        try {
            HttpEntity<Map> requestEntity = new HttpEntity<Map>(map, requestHeaders);
            ResponseEntity<JSONObject> response = restTemplate.exchange(URL_FIRST + domain + CLOUD_CAMPUS_TOKEN_URL, HttpMethod.POST, requestEntity, JSONObject.class);
            return response.getBody().getJSONObject("data").getString("token_id");
        } catch (Exception e) {
            log.error("debug Token ==>" + e.getMessage());
            return null;
        }
    }
    /**
     * 获取站点site
     *
     * @param requestHeaders
     * @return
     */
    public List<SiteDto> getSites(HttpHeaders requestHeaders, String domain) {
        //获取站点
        int pageIndex = 1;
        List<SiteDto> siteDtos = new ArrayList<>();
        try {
            HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
            ResponseEntity<JSONObject> response = restTemplate.exchange("https://" + domain + SITE_URL + pageIndex, HttpMethod.GET, requestEntity, JSONObject.class);
            JSONArray data = response.getBody().getJSONArray("data");
            if (!data.isEmpty()) {
                siteDtos.addAll(data.toJavaList(SiteDto.class));
            }
            return siteDtos;
        } catch (Exception e) {
            log.error("debug getSites ==>" + e.getMessage());
            return null;
        }
    }
    /**
     * 根据强制登录标识符获取token
     * 强制标识符不存在且没有用户登录则直接返回token
     * 用户已登陆则直接返回用户重复登录码
     * @param loginUserDto
     * @param httpServletRequest
     * @param map
     * @param userInfo
     * @return
     * @throws Exception
     */
    private HashMap getHashMap(LoginUserDto loginUserDto, HttpServletRequest httpServletRequest, HashMap map, UserInfo userInfo) throws Exception {
        String realIp = getRealIp(httpServletRequest);
        UserDto userDto = ModelMapperUtil.strictMap(userInfo, UserDto.class);
        userDto.setIp(realIp); // 来源ip
        userDto.setDate(new Date()); //登录时间
        String token = "";
        Boolean isLogin = loginUserDto.getIsLogin();
        Set<String> strings = redisUtil.redisLike(userInfo.getId());
        if (isLogin != null) {
            if (isLogin) { // 强制登录标识符
                token = UserTokenManager.generateToken(userInfo.getId());
                UserInfoController.clearCache(strings, redisUtil);
                HashMap map1 = setUserInfo(map, userInfo, userDto, token);
                return map1;
            }
        }
        if (strings.size() >= 1) {
            map.put("errorCode","用户重复登陆");
            return map ;
        }else {
            token = UserTokenManager.generateToken(userInfo.getId());
            HashMap map1 = setUserInfo(map, userInfo, userDto, token); //更新用户信息
            return map1;
        }
    }

    /**
     * 更新用户信息并返回token
     * @param map
     * @param userInfo
     * @param userDto
     * @param token
     * @return
     * @throws Exception
     */
    private HashMap setUserInfo(HashMap map, UserInfo userInfo, UserDto userDto, String token) throws Exception {
        userInfo.setCount(userInfo.getCount()+1);
        userInfo.setDate(userDto.getDate());
        userInfo.setIp(userDto.getIp());
        userInfoService.update(userInfo);
        redisUtil.setValue(token+userInfo.getId(), JsonXMLUtils.obj2json(userDto)); // 存入缓存
        map.put("Token",token);
        return map;


    }

    /**
     * 用户名获取邮箱用于密码找回
     * @param username
     * @return
     * @throws Exception
     */
    private UserInfo getUserInfoDto(UserInfoDto username) throws Exception {
        UserInfoDto userInfoDto = new UserInfoDto();
        MailBean mailBean = new MailBean();
        UserInfo userInfo = userInfoDao.queryUserByName(username.getUsername());
        if (userInfo == null) {
            return null;
        }else {
            UserExtend userExtend = userExtendDao.queryByUserId(userInfo.getId()); // 信息是否完善
            if (userExtend.getEmail() != null && !userExtend.getEmail().equals("")
                    && userExtend.getEmail().contains("@")) { // 邮箱是否存在格式是否正确
                String random = getRandom();
                mailBean.setSubject("密码找回，请点击链接进行操作");
                mailBean.setRecipient(userExtend.getEmail());
                mailBean.setContent("您的验证码："+random+"\r\n"+"请点击下列链接并填写验证码："+url);
                userInfoDto = JsonXMLUtils.json2obj(JsonXMLUtils.obj2json(userInfo),UserInfoDto.class);
                userInfoDto.setUserExtend(userExtend);
                redisUtil.set(random,1, JsonXMLUtils.obj2json(userInfoDto),10); // 缓存两分钟有效
                redisTemplate.convertAndSend("sendEmail",mailBean); // 发送邮件
                return userInfo;
            }
            return null;
        }
    }

    /**
     *  新增用户
     * @param userInfoDto
     * @param realIp
     * @return
     */
    private UserInfo getUserInfo(UserInfoDto userInfoDto, String realIp) {
        UserInfo userInfo = ModelMapperUtil.strictMap(userInfoDto,UserInfo.class);
        UserInfo userInfo1 = userInfoDao.queryUserByName(userInfo.getUsername());
        if (userInfo1 != null) {
            return null;
        }
        Result result = feginMstscUnique.addApiAccount();
        if (result.getCode() == 200) {
            userInfo.setId(result.getData().toString());
        }else {
            userInfo.setId(IdUtil.getStringId());
        }
        userInfo.setIp(realIp);
        userInfo.setAddTime(new Date());
        userInfo.setDate(new Date());
        userInfo.setCount(1);
        userInfo.setStatus("0");
        userInfoDao.insert(userInfo);
        UserExtend userExtend = new UserExtend();
        Result result1 = feginMstscUnique.addApiAccount();
        if (result1.getCode() == 200) {
            userExtend.setId(result1.getData().toString());
        }else {
            userExtend.setId(IdUtil.getStringId());
        }
        userExtend.setUserId(userInfo.getId());
        userExtendDao.insert(userExtend);
        return userInfo;
    }

    /**
     * 递归查询缓存中存在验证码
     * @return
     */
   private  String getRandom(){
       String randomNum = CharUtil.getRandomNum(6);
       if (redisUtil.exist(randomNum)) {
           getRandom();
       }
       return randomNum;
    }
    public String getRealIp(HttpServletRequest request) {
        return getString(request);

    }
    public static String getString(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        ip = getString(request, ip);
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    public static String getString(HttpServletRequest request, String ip) {
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        return ip;
    }
}
